<?php
// DEPRECATED: HAS A LOT OF BUG!
define('BASE_EVAL_PATH', realpath(dirname(__FILE__) . '/../../temp/'));

class Evaluator {
	private $task;
	private $submission;

	private $exe_path;

	public function __construct(TaskObject $task, SubmissionObject $submission) {
		$this->task = $task;
		$this->submission = $submission;
	}

	public function compile_submission() {
		global $conf;

print_r($this->submission);

		$this->submission->status = COMPILING;
		$this->submission->update();

		$filepath = $this->get_file_path();
		$out = BASE_EVAL_PATH . '/bin/' . $this->submission->id;

		$command = ($this->submission->lang == 'c') ? $conf['compile-command']['c'] : $conf['compile-command']['cpp'];
		$command = sprintf($command, $out, $filepath) . ' 2>&1';
pf($command);

		set_bin_dir();

		$descriptorspec = array(
			0 => array("pipe", "r"),
			1 => array("pipe", "w")
		);

		$process = proc_open($command, $descriptorspec, $pipes);

		if (is_resource($process)) {
			fclose($pipes[0]);
			$out = stream_get_contents($pipes[1]);
			fclose($pipes[1]);
			$return_value = proc_close($process);

			// set the compiler output;
			$this->submission->compile_message = $out;

			if (!file_exists($out)) {
				// Compile error
				$this->submission->status = COMPILE_ERROR;
				$this->submission->update();
				return false;
			}
			$this->submission->update();
		} else {
			return $this->grader_error('ERROR FFFF:0001 : Cannot compile the source code!');
		}
		return true;
	}

	public function evaluate() {
		global $conf;
		$command = sprintf($conf['sandbox'],
			$this->task->mem_limit, $this->task->mem_limit,
			$this->task->allow_file, $this->task->time_limit, $this->submission->id);
		
		$this->submission->status = GRADING;
		$this->submission->update();
			
		set_bin_dir();
		
		if (!file_exists('sandbox')) {
			// Copy sandbox over from the sandbox folder
			$source = dirname(__FILE__) . '/../../sandbox/sandbox';
			$target = './sandbox';
			if (!file_exists($source)) {
				return $this->grader_error('ERROR FFFE:0001 : Sandbox not found!');
			}
			$result = copy($source, $target);
			if ($result === false) {
				return $this->grader_error('ERROR FFFE:0001 : Sandbox uncopiable!');
			}
		}
		
		$metadata = array();
		$result = '';
		
		$taskfolder = realpath(dirname(__FILE__) . '/../../tasks') . '/' . $this->task->slug;
		if (file_exists($taskfolder)) {
			// Process to get all output;
			
			for ($i = 1; $i <= $this->task->numtestcase; $i++) {
				$filename = "$taskfolder/$i.in";
				$solution = "$taskfolder/$i.out";
				$stdout = "./$i.o";
				
				if (!file_exists($filename) ) {
					return $this->grader_error('ERROR FFEE:0001 : Input ' . $i . ' not found!');
				}
				
				if (!file_exists($solution) ) {
					return $this->grader_error('ERROR FFEE:0002 : Output ' . $i . ' not found!');
				}
				
				$result = copy($filename, '.stdin');
				$result &= copy($solution, 'task.sol');
				
				if (!$result) {
					return $this->grader_error('ERROR FFEA:0001 : Cannot copy i/o set #' . $i);
				}
				
				// Execute user solution
				
				$descriptorspec = array(
					0 => array("pipe", "r"),
					1 => array("pipe", "w"),
					2 => array("pipe", "w")
				);
		
				$process = proc_open($command, $descriptorspec, $pipes);
		
				if (is_resource($process)) {
					fclose($pipes[0]);
					$out = stream_get_contents($pipes[1]);
					fclose($pipes[1]);
					$err = stream_get_contents($pipes[2]);
					fclose($pipes[2]);
					$return_value = proc_close($process);
					
					$out_arr = explode("\n", trim($out));
					
					$t = '';
					
					foreach ($out_arr as $line) {
						if ($t = strstr($line, 'status')) { // Got status message
							$t = substring($t, 7);
							$t = trim($t);
						}
					}

					print_r($out_arr);
					$status = array();
					foreach ($out_arr as $line) {
						$a = explode(':', $line);
						$status[$a[0]] = $a[1];
					}
					
					if (!isset($status['time']) || !isset($status['time-wall']) || !isset($status['mem'])) {
						$this->grader_error('ERROR FFCC:0002 : Error in sandbox program!');
					} else {
						$metadata[$i] = array(
							'time' => $status['time'],
							'mem' => $status['mem']
						);
					}
					
					if (!empty($t)) {
						if ($t == 'TO') {
							// Time limit exceed
							$result .= 'T';
							$metadata[$i]['result'] = 'T';
						} else {
							// program crash
							$result .= 'X';
							$metadata[$i]['result'] = 'X';
						}
					} else {
						if ($this->checkAns('.stdout', $solution)) {
							$result .= 'P';
							$metadata[$i]['result'] = 'P';
						} else {
							$result .= '-';
							$metadata[$i]['result'] = '-';
						}
					}
					
				} else {
					return $this->grader_error('ERROR FFCC:0001 : Unable to run the target application!');
				}
			}
		} else {
			return $this->grader_error('ERROR FFEF:0001 : Task folder not found!');
		}
		
		$passed = true;
		$pass = 0;
		
		$usage = 0;
		$mem = 0;

		$result = '';
		
		foreach ($metadata as $meta) {
			if ($meta['result'] != 'P') $passed = false;
			else $pass++;
			$result .= $meta['result'];
			$usage = max($usage, $meta['time']);
			$mem = max($mem, $meta['mem']);
		}

		pf("\n\n\n$pass\n\n\n");
		
		$this->submission->score = $pass/$this->task->numtestcase;
		$this->submission->usage = $usage;
		$this->submission->mem = $mem;
		
		if ($passed) $this->submission->status = PASSED;
		else  $this->submission->status = FAILED;

		$this->submission->task_metadata = serialize($metadata);
		$this->submission->result = $result;
		$this->submission->update();
	}
	
	private function checkAns($f1, $f2) {
		$c1 = file($f1);
		$c2 = file($f2);
		
		foreach ($c1 as $i=>$l) {
			$l  = trim($l);
			$l2 = trim($c2[$i]);
			$l  = preg_replace('/[\s]+/',' ',$l);
			$l2 = preg_replace('/[\s]+/',' ',$l2);
			if ($l != $l2) return false;
		}
		return true;
	}

	private function get_file_path() {
		$base = BASE_EVAL_PATH . '/';
		$filename = $this->submission->id;
		return $base . $filename . '.source';
	}
	
	private function grader_error($msg) {
		$this->submission->output = $msg;
		$this->submission->status = GRADER_ERROR;
		$this->submission->update();
		return false;
	}
}
